{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Abstractive Text Summarization with Seq2Seq Using PyTorch\n",
    "This notebook aims to perform abstractive text summarization for article-title pairs using PyTorch. In other words the goal is to make use of the `article-title` pair information to train a seq2seq model for predicting (summarizing) a suitable title for a news story. Abstractive text summarization is challenging because in this scenario the model can generate a summary based on an actual abstracted summary and can even use words that are not present in the original input. The techniques that will be used in this tutorial are adopted from the paper [\"Abstractive Text Summarization using Sequence-to-sequence RNNs and Beyond\"](https://arxiv.org/pdf/1602.06023.pdf).\n",
    "\n",
    "In this tutorial you will learn the following:\n",
    "- Implement LSTMs, GRUs, biLSTMS, etc.\n",
    "- Text processing using spaCy\n",
    "- Batch data processing\n",
    "- Implement attention mechanism\n",
    "- Use pretrained vectors from GloVe\n",
    "- Sample with attention plots"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "Let's first install all the necessary libraries such as PyTorch and fastai."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "#!pip install fastai==0.7.0\n",
    "#!pip install torch==0.4.1\n",
    "#!pip install spacy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Data Exploration\n",
    "Before diving deep with your models and data, it's always important to explore your data and understand its properties. To load our data, we are going to use [torchtext](https://github.com/pytorch/text), but you can choose to use any data processing library you wish. Torchtext will allow us to keep this tutorial simple since it already offers a lot of functions, such as padding and numericalization, to process our dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[93m    Linking successful\u001b[0m\n",
      "    /home/ellfae/anaconda3/lib/python3.6/site-packages/en_core_web_sm -->\n",
      "    /home/ellfae/anaconda3/lib/python3.6/site-packages/spacy/data/en\n",
      "\n",
      "    You can now load the model via spacy.load('en')\n",
      "\n"
     ]
    }
   ],
   "source": [
    "#import data, vocab from torchtext \n",
    "from torchtext import data, vocab\n",
    "import spacy.cli\n",
    "spacy.cli.download(\"en\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# directory path to dataset\n",
    "DATA_PATH = 'data/'\n",
    "SAMPLE_DATA_PATH = f'{DATA_PATH}sample_data/'\n",
    "PROCESSED_DATA_PATH = f'{DATA_PATH}processed_data/'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One thing to keep in mind is that this will be a computation heavy NLP task. So we would like to first test our models with a sample of the original dataset. We have generated a sample of our training and testing datasets already. You can also obtain the original dataset used for this tutorial [here](https://drive.google.com/file/d/0B6N7tANPyVeBNmlSX19Ld2xDU1E/view). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the code below, we are using the spaCy tokenizer and we are converting our dataset to a nice format. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "tokenizer = data.get_tokenizer('spacy')\n",
    "TEXT = data.Field(tokenize=tokenizer, lower=True, eos_token='_eos_')\n",
    "trn_data_fields = [(\"source\", TEXT),\n",
    "                   (\"target\", TEXT)]\n",
    "\n",
    "trn, vld = data.TabularDataset.splits(path=f'{SAMPLE_DATA_PATH}',\n",
    "                                     train='train_ds.csv', validation='valid_ds.csv',\n",
    "                                     format='csv', skip_header=True, fields=trn_data_fields)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Source: ['the', 'government', 'credited', 'new', 'zealand', \"'s\", 'reputation', 'for', 'having', 'a', 'pristine', 'environment', 'for', 'the', 'nearly', '#', 'percent', 'rise', 'in', 'tourism', 'revenue', 'for', 'the', 'year', 'through', 'march', '#', '#', '#', '#', 'to', 'a', 'record', 'of', 'more', 'than', 'us$', '#', '#', 'billion', '-lrb-', 'nz$', '#', '#', 'billion', '-rrb-', '.']\n",
      "Target: ['new', 'zealand', 'tourism', 'revenue', 'rises', 'nearly', '#', 'percent', 'to', 'record', 'us$', '#', '#', '.', '#', 'billion']\n",
      "Source: ['rep', '.', 'john', 'doolittle', \"'s\", 'practice', 'of', 'paying', 'a', '#', '#', 'percent', 'fundraising', 'commission', 'to', 'a', 'company', 'owned', 'by', 'his', 'wife', ',', 'julie', ',', 'has', 'come', 'under', 'attack', 'from', 'a', 'professional', 'association', 'of', 'fundraisers', '.']\n",
      "Target: ['group', 'challenges', 'doolittle', 'wife', 'on', 'fundraising']\n",
      "Source: ['the', 'government', 'raised', 'the', 'nationwide', 'terror', 'alert', 'to', 'its', 'second', '-', 'highest', 'level', ',', 'closed', 'nine', 'u.s', '.', 'embassies', 'overseas', 'and', 'heightened', 'security', 'at', 'federal', 'buildings', 'and', 'landmarks', 'in', 'america', 'as', 'new', 'intelligence', 'warned', 'of', 'car', 'bombings', ',', 'suicide', 'attacks', 'and', 'other', 'strikes', 'linked', 'to', 'the', 'sept', '.', '#', '#', 'anniversary', '.']\n",
      "Target: ['bush', 'administration', 'warns', 'of', 'heightened', 'threat', 'for', 'terrorist', 'strike']\n"
     ]
    }
   ],
   "source": [
    "# Let's print out an example using the loaders we defined above\n",
    "for i in range(3):\n",
    "    print(\"Source:\", trn[i].source)\n",
    "    print(\"Target:\", trn[i].target)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that the instances are already tokenized. That's great! Let's just get back the original to see the examples more clearly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Source: the government credited new zealand 's reputation for having a pristine environment for the nearly # percent rise in tourism revenue for the year through march # # # # to a record of more than us$ # # billion -lrb- nz$ # # billion -rrb- .\n",
      "Target: new zealand tourism revenue rises nearly # percent to record us$ # # . # billion\n",
      "\n",
      "\n",
      "Source: rep . john doolittle 's practice of paying a # # percent fundraising commission to a company owned by his wife , julie , has come under attack from a professional association of fundraisers .\n",
      "Target: group challenges doolittle wife on fundraising\n",
      "\n",
      "\n",
      "Source: the government raised the nationwide terror alert to its second - highest level , closed nine u.s . embassies overseas and heightened security at federal buildings and landmarks in america as new intelligence warned of car bombings , suicide attacks and other strikes linked to the sept . # # anniversary .\n",
      "Target: bush administration warns of heightened threat for terrorist strike\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for i in range(3):\n",
    "    print(\"Source:\", \" \".join(trn[i].source))\n",
    "    print(\"Target:\", \" \".join(trn[i].target))\n",
    "    print(\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There you go! Those are our `article-title` pairs."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Preprocess the dataset\n",
    "Before we start setting up our models, we need to pre-process our dataset and transform it in a way that the models can understand. We will do things like tokenizing, generating vocabulary, creating dataset iterators, batching, padding, and much more."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Build Vocabulary\n",
    "One key step in any NLP problem, is to build a vocabulary from our dataset, since these essentially become the input features in our model. Well, they are the basis of our input features, but I will discuss later in the tutorial what I mean by that. For now, let's try to generate a vocabulary from the datasets.\n",
    "\n",
    "In building the vocabulary, we want to use it to generate `token->index` pairs. This way we associate each token in the article and titles with an integer value, which will be used to generate word embedding features. \n",
    "\n",
    "Now that I mentioned the word embeddings, these will represent the input features of our models. We will use pretrained word embeddings from GloVe in this tutorial."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# load the pretrained embeddings and build the vocabulary\n",
    "# Notice that we are only using the training dataset here!\n",
    "# NOTE: The embeddings will take a couple minutes to download\n",
    "pre_trained_vector_type = 'glove.6B.200d' \n",
    "TEXT.build_vocab(trn, vectors=pre_trained_vector_type )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('#', 151222),\n",
       " ('the', 130963),\n",
       " ('.', 105044),\n",
       " (',', 85365),\n",
       " ('to', 83863),\n",
       " ('in', 77726),\n",
       " ('of', 77411),\n",
       " ('a', 71221),\n",
       " ('on', 43194),\n",
       " ('and', 42839)]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#10 most frequent words in the vocab\n",
    "TEXT.vocab.freqs.most_common(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Dataset Iterator / Batches / Tensors / Padding \n",
    "The first thing we want to do with our datasets is to covert them to tensor objects using the vocabulary indices we generated above. We would like those vectors in the tensors to be padded (with similar lengths placed together). In order to efficiently iterate over the datasets, and to create batches, we will use the `data.BucketIterator.splits` function available from the `data` module in `torchtext`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using cuda\n",
      "Torch version: 0.4.1\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "USE_GPU = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
    "print(\"Using {}\".format(USE_GPU))\n",
    "print(\"Torch version: {}\".format(torch.__version__))\n",
    "\n",
    "# batches\n",
    "batch_size = 32\n",
    "train_iter, val_iter = data.BucketIterator.splits(\n",
    "                        (trn, vld), batch_sizes=(batch_size, int(batch_size*1.6)),\n",
    "                        device= -1,#(0 if USE_GPU else -1), \n",
    "                        sort_key=lambda x: len(x.source),\n",
    "                        shuffle=True, sort_within_batch=False, repeat=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once we have the batch iterators, we convert `article-title` pairs into `source-target` tuples (article, title), which simplifies things when training time arrives. This is no different from pairing a document to a label in the sentiment classification setting, just that in this case the target is the title and the source is the article."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "class BatchTuple():\n",
    "    def __init__(self, dataset, x_var, y_var):\n",
    "        self.dataset, self.x_var, self.y_var = dataset, x_var, y_var\n",
    "        \n",
    "    def __iter__(self):\n",
    "        for batch in self.dataset:\n",
    "            x = getattr(batch, self.x_var) \n",
    "            y = getattr(batch, self.y_var)                 \n",
    "            yield (x, y)\n",
    "            \n",
    "    def __len__(self):\n",
    "        return len(self.dataset)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# returns tuple of article-title pair tensors\n",
    "train_iter_tuple = BatchTuple(train_iter, \"source\", \"target\")\n",
    "val_iter_tuple = BatchTuple(val_iter, \"source\", \"target\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# an example of a batched and padded article-title tensor pair\n",
    "sample = next(iter(train_iter_tuple))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[   81,     4,  1672,     4,   785, 11068,    53,  2998,    11,  2082,\n",
       "             22,   176,   653,  3470, 17098,  2382,  1251,     4,     4,    10,\n",
       "            419,    15,   394,  3923,  8372,    15,   226,    92,    25,    10,\n",
       "            411,     8],\n",
       "         [  443,   480, 16866,  1295,   872,  2609,  2702,    12,   637,    35,\n",
       "             24,   712,  6258,  4113,  7940,    12,   452, 16131,    41,  8435,\n",
       "           1474,     4,    35,  6899,  2734,  2356,     8,   410,   760,   198,\n",
       "             13,  1661],\n",
       "         [  142,   137,     6,  2739,  1341,  4399,   567,  6192,     3,  1093,\n",
       "             23,  3110,  1767,    14,    38,  1122, 23402,  3486,     5,  1517,\n",
       "              4,    51,   152,    12,     6, 13560,  1571,  4924,    38,    38,\n",
       "            214,     6],\n",
       "         [  608,   116,    18,   363,   532,   241,    61,  1201,     3,     7,\n",
       "          10618,     8,     7,  4828,  3470,    13,     6,    15,   149,    80,\n",
       "            250,   136, 15601,     4,     4,     6,  4223,  6294,  4200,  1978,\n",
       "             40,  1561],\n",
       "         [  172,   243,  4266,    35,   286,  2716,  1398,  3009,     6,    42,\n",
       "             35,   422,   640,  1848,  1197,  8307, 24664,  5920,   816,  1467,\n",
       "            314,   173,    11,  2309,  4533,     4,    32,   581,  2471,  1701,\n",
       "             52,  1371],\n",
       "         [   39,  2186, 24331,    46,    20,     4,    54,  1024,    60,   183,\n",
       "              3,   366,    10,    89,     7,    36,     6,  7476,     7,   188,\n",
       "              9,   363,     4,     3,  2349,   812,     7,    69,   158,     8,\n",
       "           3703,   775],\n",
       "         [   20,    11,     6,     6,   785,   291,     7,   172,    40,    12,\n",
       "            100,    55,   911,   263,    52,  2931,  8073,    38,   545,  5431,\n",
       "           1803,     6,   669,     3,  1537,  4221,  3518,    33,    12,  4257,\n",
       "              4,    15],\n",
       "         [ 5919,    31,    35,    84,  4478,    19,    36,    33,   496,   547,\n",
       "            230,  1846,  1965,  2746,  2659,     9,     6,   536,    58,   234,\n",
       "              8,     4,   113,  4118,   860,   881,  4297,    17,     4,  2524,\n",
       "          14676,     4],\n",
       "         [ 1442,    15,  1576,     4,   510,     4,  8353,    11,   108,    74,\n",
       "             12,  3012,   120,    13,   632,  2423, 17218,   238,     4,     9,\n",
       "             36,  3259,     6, 19369,     6,     7,  2661,   999,   267,     8,\n",
       "             39,   367],\n",
       "         [ 2781,    10,   331, 35603,    21, 14039,     6,     4,     8,     4,\n",
       "             60,  1126,   442,  4113,    19,   495,    58,     4,   562,  2238,\n",
       "             51,  5715,  1337,     4,    28,    52,   510,  2752,   115,  5617,\n",
       "            315,   192],\n",
       "         [ 7462,   261,   454,    35,   286,  3129,    22,  1163,   391,   524,\n",
       "             13,   717,  1301, 11003,     4, 23460,     4,  4723,    11,  9073,\n",
       "           1580,  1611,     4, 32312,  2322,    10,   259,   869,    12,  1303,\n",
       "             19,    28],\n",
       "         [    4,   892,    14,   414,    11,   593,    24,   645,    17,    14,\n",
       "           9612,     8,    83,    57,    50,  2545,   178,  2684,    31,     6,\n",
       "            228,  1099, 37374,    26,   106,   792,  1309,   118,   396,     6,\n",
       "            237,    10],\n",
       "         [  161,   825,  1919,  1980,     4,    11,    23,     9,    34,  1221,\n",
       "              9,    55,     9,    55,  4282,   952,     9,   170,     8,   226,\n",
       "            209,     7,    57,  1757,  8946, 11395,     9,    56,   993,    63,\n",
       "              8,   265],\n",
       "         [    5,     5,     5,     5,  3016,    67,   461,   130,   239,   700,\n",
       "           1599,  4741,   936,   441,   761,   654,   984, 20962,   542,    16,\n",
       "             11,  1788,  1191,   911,    17,  5930,     4, 11434,    11,    16,\n",
       "             48,  1007],\n",
       "         [    2,     2,     2,     2,     5,     5,     5,     5,     5,     5,\n",
       "              5,     5,     5,     5,     5,     5,     5,     5,     5,     5,\n",
       "             37,  1231,  2370,  1090,  4694, 12973,    78, 33582,    31,    33,\n",
       "           2388,   224],\n",
       "         [    1,     1,     1,     1,     2,     2,     2,     2,     2,     2,\n",
       "              2,     2,     2,     2,     2,     2,     2,     2,     2,     2,\n",
       "              5,     5,     5,     5,     5,     5,     5,     5,     5,     5,\n",
       "              5,     5],\n",
       "         [    1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              2,     2,     2,     2,     2,     2,     2,     2,     2,     2,\n",
       "              2,     2]]),\n",
       " tensor([[ 2060,   875,   176,  1758,     4,  2609,  2702,  2998,  1186,  2082,\n",
       "           1599,  1196,  6258,  4113,  7940,  2382,     4,  2684,   149,  8435,\n",
       "            327,  9856, 18725,     3,  8372, 13560,  4297,    92,   760,   198,\n",
       "            411,    47],\n",
       "         [  203,  5684,  1672, 35603,  6806,  5823,   567,  6192,    34,    79,\n",
       "           1311,   376,    12,    14,   881,    12, 42642,  3075,   112,   996,\n",
       "           8758,  1315,    46,     3,  3050,    13,  1654,   410,  4200,   343,\n",
       "             13,  1788],\n",
       "         [  159,     9,  1650,   732,     9,  3129,  2339,  1201,  1117,   524,\n",
       "             15,   529,  1672,  4828,  2818,  1122,    66,  3120,     7,  2238,\n",
       "             93,  4429,     4,  4118,  8073,   885,     8,   829,     8,     8,\n",
       "            214,    57],\n",
       "         [ 1052,   530,    74,     7,    10,   291,    84,  1649,    15,    14,\n",
       "           3455,   216,    57,   418,     8,    13,    74,   198,   204,  1467,\n",
       "            419,  1788,   162,     3,  5940,  3008,  1571,  2752,   158,  4257,\n",
       "              7,   231],\n",
       "         [  172,    14,  8970,  2968, 15507,  1197, 36520,   172,   131,  1221,\n",
       "             66,  6100,   106,   806,     3,   858,  4385,     2,     3,  1102,\n",
       "              2,   337,   113,     3,  3349, 10274,   144,   869,   267,  3283,\n",
       "           4820,     7],\n",
       "         [    2,   809,   747,     2,    11,    77,  5531,     2,    14,   700,\n",
       "           1934,  1846,  2249,     4,     3,  2931,   984,     1,     3,     2,\n",
       "              1,     2,     2,  6922,     2,  3799,   111,   638,   115,   800,\n",
       "          14676,   681],\n",
       "         [    1,   142,     2,     1,     4,   250,  2546,     1,   268,    40,\n",
       "           2469,     7,  2084,  4113,  2973,    11,     2,     1,     3,     1,\n",
       "              1,     1,     1,     3,     1,  1489,  5431,     2,   396,  2245,\n",
       "             19,   265],\n",
       "         [    1,   122,     1,     1,  3016,   314,     2,     1,   446,   341,\n",
       "           2662,   475,    10,  2746,  1215, 23460,     1,     1,   562,     1,\n",
       "              1,     1,     1,     3,     1,     2,     2,     1,     2,     2,\n",
       "            373,  1007],\n",
       "         [    1,    31,     1,     1,     2,     2,     1,     1,     2,  1048,\n",
       "            747,  1436,   892, 11003,    19,    11,     1,     1,     8,     1,\n",
       "              1,     1,     1,     2,     1,     1,     1,     1,     1,     1,\n",
       "           2950,     2],\n",
       "         [    1,  2607,     1,     1,     1,     1,     1,     1,     1,     7,\n",
       "             10,     2,   675,   285,    50,  1175,     1,     1,   542,     1,\n",
       "              1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "            546,     1],\n",
       "         [    1,     5,     1,     1,     1,     1,     1,     1,     1,   360,\n",
       "             59,     1,     2,     2,  4282,  3632,     1,     1,     2,     1,\n",
       "              1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              2,     1],\n",
       "         [    1,     3,     1,     1,     1,     1,     1,     1,     1,     2,\n",
       "            365,     1,     1,     1,   761,     2,     1,     1,     1,     1,\n",
       "              1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              1,     1],\n",
       "         [    1,     3,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "            693,     1,     1,     1,     2,     1,     1,     1,     1,     1,\n",
       "              1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              1,     1],\n",
       "         [    1,     2,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "          18184,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              1,     1],\n",
       "         [    1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "           2554,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              1,     1],\n",
       "         [    1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              2,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              1,     1,     1,     1,     1,     1,     1,     1,     1,     1,\n",
       "              1,     1]]))"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# max_len X batch_size\n",
    "sample # article and title tensor pair"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([17, 32])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sample[0].size()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([  81,  443,  142,  608,  172,   39,   20, 5919, 1442, 2781, 7462,    4,\n",
      "         161,    5,    2,    1,    1])\n",
      "tensor([2060,  203,  159, 1052,  172,    2,    1,    1,    1,    1,    1,    1,\n",
      "           1,    1,    1,    1])\n"
     ]
    }
   ],
   "source": [
    "# the following lines obtain the first article-tile pair in the batch sample\n",
    "# PAY ATTENTION to the dimensions\n",
    "print(sample[0].permute(-1,0)[0])\n",
    "print(sample[1].permute(-1,0)[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Source:  south korean stocks ended higher friday as strengthened blue chips bolstered the market . _eos_ <pad> <pad>\n",
      "Target:  seoul shares close slightly higher _eos_ <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad>\n"
     ]
    }
   ],
   "source": [
    "# let's print their original information\n",
    "print(\"Source: \", \" \".join([TEXT.vocab.itos[i] for i in sample[0].permute(-1,0)[0]]))\n",
    "print(\"Target: \", \" \".join([TEXT.vocab.itos[i] for i in sample[1].permute(-1,0)[0]]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The examples above are just meant to show you what all the text processors are really doing with our data. The tensors are in raw integers so we needed to convert them back to strings using the `TEXT.vocab.itos[]` function."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create ModelData\n",
    "In this tutorial we are also going to use a library known as `fastai`, which helps to handle our data well, and train/inspect how our models are performing. Fastai contains a lot of neat functions that are commonly used best practices and built on top of PyTorch, which simplifies coding and makes it easy to train models."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn.functional as F\n",
    "import torch.nn as nn\n",
    "from fastai.text import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ModelData puts together the dataset into one single object for ease of manipulation\n",
    "model_data = ModelData(SAMPLE_DATA_PATH, trn_dl=train_iter_tuple, val_dl=val_iter_tuple)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can actually view our tensor objects, similar to how we did above, but the great thing now is that we have access to both train and test tensors from one object. Let's sample a batch from train and a similar analysis like above on a `article-title` pair. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2500, 393, 52380)"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# number of batches in training & validation set and number of tokens in vocabulary\n",
    "len(model_data.trn_dl), len(model_data.val_dl), len(TEXT.vocab)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(torch.Size([40, 32]), torch.Size([25, 32]))"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# shape of one batch in training set (sequence_length x batch_size)\n",
    "t, z = next(model_data.trn_dl.__iter__())\n",
    "t.size(), z.size()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "source:\n",
      "washington -- as the nation 's first major party african american nominee for president , democrat barack obama would be testing the audacity of hope in his effort to wrest large blocks of the old confederacy from republicans . _eos_ \n",
      "\n",
      "corresponding tensor:\n",
      "[  391   183    20     4   268    13    51   184   132   221   146  3175    15    34     6  2544   604   274\n",
      "   102    52  2166     4 38114     9   942     8    29   740     7 14729   861  4575     9     4   230 20046\n",
      "    21  1234     5     2] \n",
      "\n",
      "target:\n",
      "obama hopes to rise in south _eos_ <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> <pad> \n",
      "\n",
      "corresponding tensor:\n",
      "[274 609   7 430   8  81   2   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1] \n",
      "\n"
     ]
    }
   ],
   "source": [
    "#lets look at an example pair\n",
    "sample_source = t.transpose(1,0)[0].data.cpu().numpy()\n",
    "sample_target = z.transpose(1,0)[0].data.cpu().numpy()\n",
    "\n",
    "print(\"source:\\n%s \\n\\ncorresponding tensor:\\n%s \\n\" %(' '.join([TEXT.vocab.itos[o] \n",
    "                                                                 for o in sample_source]), sample_source))\n",
    "print(\"target:\\n%s \\n\\ncorresponding tensor:\\n%s \\n\" %(' '.join([TEXT.vocab.itos[o] \n",
    "                                                                 for o in sample_target]), sample_target))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Okay! Enough messing around. Let's get to what you came here for. The models!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Model\n",
    "We are using a simple encoder-decoder architecture based on RNN. We are also using an attention layer based on the work [Luong et al., 2015](https://arxiv.org/pdf/1508.04025.pdf). Below are the specific things we will be implementing for our model:\n",
    "\n",
    "* model architecture supports LSTM & GRU (biLSTM-to-uniLSTM or biGRU-to-uniGRU)\n",
    "* implements attention mechanism ([Bahdanau et al.](https://arxiv.org/abs/1409.0473) & [Luong et al.(global dot)](https://arxiv.org/abs/1508.04025))\n",
    "* implements [scheduled sampling (teacher forcing)](https://arxiv.org/abs/1506.03099)\n",
    "* implements [tied embeddings](https://arxiv.org/pdf/1608.05859.pdf)\n",
    "* initializes encoder-decoder with pretrained vectors (glove.6B.200d)\n",
    "* implements attention plots"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Seq2SeqRNN(nn.Module):    \n",
    "    \n",
    "    def __init__(self, rnn_type, input_size, embz_size, hidden_size, batch_size,output_size,max_tgt_len,\n",
    "                 attention_type, tied_weight_type, pre_trained_vector, pre_trained_vector_type, padding_idx,\n",
    "                 num_layers=1, encoder_drop=(0.0,0.0), decoder_drop=(0.0,0.0), \n",
    "                 bidirectional=True, bias=False, teacher_forcing=True):\n",
    "        \n",
    "        super().__init__()\n",
    "        \n",
    "        rnn_type, attention_type, tied_weight_type = rnn_type.upper(), attention_type.title(), tied_weight_type.lower()\n",
    "        \n",
    "        if rnn_type in ['LSTM', 'GRU']: self.rnn_type = rnn_type\n",
    "        else: raise ValueError(\"\"\"An invalid option for '--rnn_type' was supplied,\n",
    "                                    options are ['LSTM', 'GRU']\"\"\")\n",
    "            \n",
    "        self.attention_type = attention_type\n",
    "       \n",
    "        if tied_weight_type in ['three_way', 'two_way']: self.tied_weight_type = tied_weight_type\n",
    "        else: raise ValueError(\"\"\"An invalid option for '--tied_weight_type' was supplied,\n",
    "                                    options are ['three_way', 'two_way']\"\"\")\n",
    "    \n",
    "                    \n",
    "        # initialize model parameters            \n",
    "        self.output_size, self.embz_size, self.hidden_size = output_size, embz_size, hidden_size//2\n",
    "        self.num_layers, self.input_size, self.max_tgt_len, self.pre_trained_vector = num_layers, input_size, max_tgt_len, pre_trained_vector\n",
    "        self.bidirectional,self.teacher_forcing, self.pre_trained_vector_type = bidirectional, teacher_forcing, pre_trained_vector_type\n",
    "        self.encoder_drop, self.decoder_drop, self.padding_idx = encoder_drop, decoder_drop, padding_idx\n",
    "        \n",
    "        if self.teacher_forcing: self.force_prob = 1.0\n",
    "        \n",
    "        # set bidirectional\n",
    "        if self.bidirectional: self.num_directions = 2\n",
    "        else: self.num_directions = 1\n",
    "            \n",
    "        # Encoder\n",
    "        self.encoder_dropout = nn.Dropout(self.encoder_drop[0])\n",
    "        self.encoder_embedding_layer = nn.Embedding(self.input_size, self.embz_size, padding_idx=self.padding_idx)\n",
    "        if self.pre_trained_vector: self.encoder_embedding_layer.weight.data.copy_(self.pre_trained_vector.weight.data)\n",
    "            \n",
    "        self.encoder_rnn = getattr(nn, self.rnn_type)(\n",
    "                           input_size=self.embz_size,\n",
    "                           hidden_size=self.hidden_size,\n",
    "                           num_layers=self.num_layers,\n",
    "                           dropout=self.encoder_drop[1], \n",
    "                           bidirectional=self.bidirectional)\n",
    "        self.encoder_vector_layer = nn.Linear(self.hidden_size*self.num_directions,self.embz_size, bias=bias)\n",
    "        \n",
    "       # Decoder\n",
    "        self.decoder_dropout = nn.Dropout(self.decoder_drop[0])\n",
    "        self.decoder_embedding_layer = nn.Embedding(self.input_size, self.embz_size, padding_idx=self.padding_idx)\n",
    "        self.decoder_rnn = getattr(nn, self.rnn_type)(\n",
    "                           input_size=self.embz_size,\n",
    "                           hidden_size=self.hidden_size*self.num_directions,\n",
    "                           num_layers=self.num_layers,\n",
    "                           dropout=self.decoder_drop[1]) \n",
    "        self.decoder_output_layer = nn.Linear(self.hidden_size*self.num_directions, self.embz_size, bias=bias)\n",
    "        self.output_layer = nn.Linear(self.embz_size, self.output_size, bias=bias)\n",
    "        \n",
    "        #set tied weights: three way tied weights vs two way tied weights\n",
    "        if self.tied_weight_type == 'three_way':\n",
    "            self.decoder_embedding_layer.weight  = self.encoder_embedding_layer.weight\n",
    "            self.output_layer.weight = self.decoder_embedding_layer.weight  \n",
    "        else:\n",
    "            if self.pre_trained_vector: self.decoder_embedding_layer.weight.data.copy_(self.pre_trained_vector.weight.data)\n",
    "            self.output_layer.weight = self.decoder_embedding_layer.weight  \n",
    "            \n",
    "        # Attention\n",
    "        self.encoder_output_layer = nn.Linear(self.hidden_size*self.num_directions, self.embz_size, bias=bias)\n",
    "        self.att_vector_layer = nn.Linear(self.embz_size+self.embz_size, self.embz_size,bias=bias)\n",
    "        if self.attention_type == 'Bahdanau':\n",
    "            self.decoder_hidden_layer = nn.Linear(self.hidden_size*self.num_directions, self.embz_size, bias=bias)\n",
    "            self.att_score = nn.Linear(self.embz_size,1,bias=bias)\n",
    "    \n",
    "    def init_hidden(self, batch_size):\n",
    "        if self.rnn_type == 'LSTM':\n",
    "            return (V(torch.zeros(self.num_layers*self.num_directions, batch_size, self.hidden_size)),\n",
    "                    V(torch.zeros(self.num_layers*self.num_directions, batch_size, self.hidden_size)))\n",
    "        else:\n",
    "            return V(torch.zeros(self.num_layers*self.num_directions, batch_size, self.hidden_size))\n",
    "   \n",
    "    def _cat_directions(self, hidden):\n",
    "        def _cat(h):\n",
    "            return torch.cat([h[0:h.size(0):2], h[1:h.size(0):2]], 2)\n",
    "            \n",
    "        if isinstance(hidden, tuple):\n",
    "            # LSTM hidden contains a tuple (hidden state, cell state)\n",
    "            hidden = tuple([_cat(h) for h in hidden])\n",
    "        else:\n",
    "            # GRU hidden\n",
    "            hidden = _cat(hidden)\n",
    "        return hidden    \n",
    "       \n",
    "    def luong_attention(self, encoder_output, decoder_output):\n",
    "        encoder_output = self.encoder_output_layer(encoder_output) \n",
    "        encoder_output = encoder_output.transpose(0,1)\n",
    "        decoder_output = decoder_output.transpose(0,1)\n",
    "        att_score = torch.bmm(encoder_output, decoder_output.transpose(-1,1))\n",
    "        att_weight = F.softmax(att_score, dim=1)\n",
    "        context_vector = torch.bmm(att_weight.transpose(-1, 1), encoder_output).squeeze(1)\n",
    "        att_vector = torch.cat((context_vector, decoder_output.squeeze(1)), dim=1)\n",
    "        att_vector = self.att_vector_layer(att_vector)\n",
    "        att_vector = torch.tanh(att_vector)\n",
    "        return att_weight.squeeze(-1), att_vector\n",
    "        \n",
    "    def decoder_forward(self, batch_size, encoder_output, decoder_hidden, y=None):\n",
    "        decoder_input = V(torch.zeros(batch_size).long())  \n",
    "        output_seq_stack, att_stack = [], []\n",
    "        \n",
    "        for i in range(self.max_tgt_len):\n",
    "            decoder_input = self.decoder_dropout(self.decoder_embedding_layer(decoder_input))\n",
    "            if self.attention_type == 'Bahdanau':\n",
    "                if isinstance(decoder_hidden, tuple):\n",
    "                    prev_hidden = self.decoder_hidden_layer(decoder_hidden[0][-1]).unsqueeze(0)\n",
    "                else:\n",
    "                    prev_hidden = self.decoder_hidden_layer(decoder_hidden[-1]).unsqueeze(0) \n",
    "                att, decoder_input = self.bahdanau_attention(encoder_output, prev_hidden, decoder_input)\n",
    "                decoder_output, decoder_hidden = self.decoder_rnn(decoder_input.unsqueeze(0), decoder_hidden)\n",
    "                decoder_output = self.decoder_output_layer(decoder_output.squeeze(0)) \n",
    "            else:\n",
    "                decoder_output, decoder_hidden = self.decoder_rnn(decoder_input.unsqueeze(0), decoder_hidden)\n",
    "                decoder_output = self.decoder_output_layer(decoder_output) \n",
    "                att, decoder_output = self.luong_attention(encoder_output, decoder_output)\n",
    "            att_stack.append(att)\n",
    "            output = self.output_layer(decoder_output)\n",
    "            output_seq_stack.append(output)\n",
    "            decoder_input = V(output.data.max(1)[1])\n",
    "            if (decoder_input==1).all(): break \n",
    "            if self.teacher_forcing:    \n",
    "                samp_prob = round(random.random(),1)\n",
    "                if (y is not None) and (samp_prob < self.force_prob):\n",
    "                    if i >= len(y): break\n",
    "                    decoder_input = y[i] \n",
    "                \n",
    "        return torch.stack(output_seq_stack), torch.stack(att_stack)\n",
    "        \n",
    "    def forward(self, seq, y=None):\n",
    "        # Encoder forward function\n",
    "        # Basically where the input enters in the architecture\n",
    "        batch_size = seq[0].size(0)\n",
    "        encoder_hidden = self.init_hidden(batch_size)\n",
    "        encoder_input = self.encoder_dropout(self.encoder_embedding_layer(seq))\n",
    "        encoder_output, encoder_hidden = self.encoder_rnn(encoder_input, encoder_hidden) \n",
    "        if self.bidirectional:\n",
    "            encoder_hidden = self._cat_directions(encoder_hidden)\n",
    "        output = self.decoder_forward(batch_size, encoder_output, encoder_hidden, y=y)\n",
    "        if isinstance(encoder_hidden, tuple):\n",
    "            encoder_vector = self.encoder_vector_layer(encoder_hidden[0][-1])\n",
    "        else:\n",
    "            encoder_vector = self.encoder_vector_layer(encoder_hidden[-1])\n",
    "        output = output + (encoder_vector,)  \n",
    "        return output"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Pretest the Seq2Seq model ?????"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tr_sample = next(model_data.trn_dl.__iter__())\n",
    "# TODO: need to find out how to get those inputs for a given batch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We would like to define what the maximum size of our target summary is going to be. It's a constraint that's necessary to train this model. We can calculate the maximum target summary size as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "19"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "its = [next(model_data.trn_dl.__iter__())[1] for i in range(10)]\n",
    "max_tgt_len = int(np.percentile([its[o].size()[0] for o in range(len(its))], 99))\n",
    "max_tgt_len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "# function to help generate our embeddings and store them as well\n",
    "def save_pickle(path, filename, file):\n",
    "    \"\"\"Function to save file as pickle\"\"\"\n",
    "    with open(f'{path}/{filename}', 'wb') as f:\n",
    "        pickle.dump(file, f)\n",
    "\n",
    "def norm_pre_trained_embeddings(vecs, itos, em_sz, padding_idx):\n",
    "    \"\"\"Function to load and normalize pretrained vectors\"\"\"\n",
    "    emb = nn.Embedding(len(itos), em_sz, padding_idx=padding_idx)\n",
    "    wgts = emb.weight.data\n",
    "    for i,w in enumerate(itos):\n",
    "        try: \n",
    "            wgts[i] = torch.from_numpy(vecs[w]-vec_mean)\n",
    "            wgts[i] = torch.from_numpy(vecs[w]/vec_std)\n",
    "        except: pass \n",
    "    emb.weight.requires_grad = False    \n",
    "    return emb\n",
    "\n",
    "def embedding_param(path, data_field, pre_trained_vector_type, embz_size=128, save_vocab=False, itos='itos', stoi='stoi'):\n",
    "    \"\"\"Returns embedding parameters\"\"\"\n",
    "    pre_trained=None\n",
    "    padding_idx = data_field.vocab.stoi['<pad>']\n",
    "    index_to_string, string_to_index = data_field.vocab.itos, data_field.vocab.stoi\n",
    "    \n",
    "    # save vocabulary\n",
    "    if save_vocab:\n",
    "        vocab_path = os.path.join(path, \"vocab\")\n",
    "        os.makedirs(vocab_path, exist_ok=True)\n",
    "        save_pickle(vocab_path, f'{itos}.pk', index_to_string) \n",
    "        save_pickle(vocab_path, f'{stoi}.pk', string_to_index) \n",
    "    \n",
    "    # generate pretrained vectors\n",
    "    if pre_trained_vector_type:\n",
    "        vec_mean, vec_std = data_field.vocab.vectors.numpy().mean(), data_field.vocab.vectors.numpy().std()\n",
    "        print('pre_trained_vector_mean = %s, pre_trained_vector_std = %s'%(vec_mean, vec_std))\n",
    "        vector_weight_matrix = data_field.vocab.vectors\n",
    "        embz_size = vector_weight_matrix.size(1)\n",
    "        pre_trained = norm_pre_trained_embeddings(vector_weight_matrix, index_to_string, embz_size, padding_idx)\n",
    "        print('Normalizing.... \\npre_trained_vector_mean = %s, pre_trained_vector_std = %s' %(pre_trained.weight.data.numpy().mean(), pre_trained.weight.data.numpy().std()))\n",
    "    return pre_trained, embz_size, padding_idx, index_to_string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rev = 1\n",
      "pre_trained_vector_mean = 0.0020214648, pre_trained_vector_std = 0.43631938\n",
      "Normalizing.... \n",
      "pre_trained_vector_mean = 6.3935506e-05, pre_trained_vector_std = 1.0000203\n"
     ]
    }
   ],
   "source": [
    "rev=0\n",
    "rev += 1\n",
    "print(\"rev = %s\" %rev)\n",
    "pre_trained_vector,  embz_size, padding_idx, index_to_string = embedding_param(SAMPLE_DATA_PATH, \n",
    "                                                                               TEXT, \n",
    "                                                                               pre_trained_vector_type, \n",
    "                                                                               save_vocab=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's inspect the information we just generated with the function above before we proceed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Embedding Matrix size:  Embedding(52380, 200, padding_idx=1)\n",
      "Embedding Dimension size:  200\n",
      "Padding Id:  1\n",
      "Vocab size:  52380\n"
     ]
    }
   ],
   "source": [
    "# the Embedding matrix\n",
    "print(\"Embedding Matrix size: \", pre_trained_vector)\n",
    "print(\"Embedding Dimension size: \", embz_size)\n",
    "print(\"Padding Id: \", padding_idx)\n",
    "print(\"Vocab size: \", len(index_to_string))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "# parameters\n",
    "input_size = len(TEXT.vocab)\n",
    "hidden_size = 400\n",
    "output_size =  len(TEXT.vocab)\n",
    "rnn_type = 'gru'\n",
    "tied_weight_type ='three_way'\n",
    "max_tgt_len = max_tgt_len"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Training\n",
    "Now that we have defined our parameters and the other embedding information, let's start to look at the code for training our seq2seq model.\n",
    "- Uses teacher forcing which is the process of training an RNN which uses the output from the prior time steps as input to the current time step. Teacher forcing helps to train the RNNs more efficiently and quickly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "from fastai.nlp import *\n",
    "from fastai.model import Stepper\n",
    "from fastai.sgdr import Callback, DecayScheduler\n",
    "from fastai.learner import Learner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Helper to train the seq2seq model\n",
    "This class below helps with training the models."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Seq2SeqStepper(Stepper):\n",
    "    def step(self, xs, y, epoch):\n",
    "        output = self.m(*xs, y)\n",
    "        xtra = []\n",
    "        if isinstance(output,tuple): output,*xtra = output\n",
    "        self.opt.zero_grad()\n",
    "        loss = raw_loss = self.crit(output, y)\n",
    "        if self.reg_fn: loss = self.reg_fn(output, xtra, raw_loss)\n",
    "        loss.backward()\n",
    "        if self.clip:   # Gradient clipping\n",
    "            nn.utils.clip_grad_norm(trainable_params_(self.m), self.clip)\n",
    "        self.opt.step()\n",
    "        return raw_loss.data\n",
    "    \n",
    "def seq2seq_loss(input, target):\n",
    "    sl,bs = target.size()\n",
    "    sl_in,bs_in,nc = input.size()\n",
    "    if sl>sl_in: input = F.pad(input, (0,0,0,0,0,sl-sl_in))\n",
    "    input = input[:sl]\n",
    "    return F.cross_entropy(input.view(-1,nc), target.view(-1))#, ignore_index=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Setting Checkpoints\n",
    "The class below helps with setting storing checkpoints for best models obtained."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "class BestModelCheckPoint(Callback):\n",
    "    def __init__(self, learner, path, model_name, lr):\n",
    "        super().__init__()\n",
    "        self.learner = learner\n",
    "        self.model_name = model_name\n",
    "        self.learning_rate = lr\n",
    "        self.model_log = {}\n",
    "        self.model_path = self.learner.models_path\n",
    "        os.makedirs(self.model_path, exist_ok=True)\n",
    "\n",
    "    def on_train_begin(self): \n",
    "        self.first_epoch = True\n",
    "        self.epoch = 0\n",
    "        self.best_loss = 0.\n",
    "\n",
    "    def on_batch_begin(self): pass\n",
    "    def on_phase_begin(self): pass\n",
    "    def on_epoch_end(self, metrics): \n",
    "        self.epoch += 1\n",
    "        self.val_loss = metrics[0]\n",
    "        if self.first_epoch:\n",
    "            self.best_loss = self.val_loss\n",
    "            self.first_epoch = False\n",
    "        elif self.val_loss < self.best_loss:\n",
    "            self.best_loss = self.val_loss\n",
    "            self.learner.save(self.model_name)\n",
    "            self.model_log['training_loss'] = [str(self.train_losses)]\n",
    "            self.model_log['validation_loss'] = [str(self.val_loss)]\n",
    "            self.model_log['epoch_num'] = [str(self.epoch)]\n",
    "            self.model_log['learning_rate'] = [str(self.learning_rate)]\n",
    "            self.model_log['model_info'] = [w for s in [str(self.learner.model)] for w in s.split('\\n')]\n",
    "            self.model_log['model_info'].append(\"(attention_type): %s\" %self.learner.model.attention_type)\n",
    "            self.model_log['model_info'].append(\"(weight_tie): %s\" %self.learner.model.tied_weight_type)\n",
    "            self.model_log['model_info'].append(\"(pre_trained_vector_type): %s\" %self.learner.model.pre_trained_vector_type)\n",
    "            self.model_log['model_info'].append(\"(teacher_forcing): %s\" %self.learner.model.teacher_forcing)\n",
    "            if self.learner.model.teacher_forcing: self.model_log['model_info'].append(\"(teacher_forcing_prob): %s\" %self.learner.model.force_prob)\n",
    "            with open(f'{self.model_path}/{self.model_name}_model_log.json', 'w') as d: json.dump(self.model_log, d)\n",
    "        else: pass        \n",
    "    def on_phase_end(self): pass\n",
    "    def on_batch_end(self, loss):\n",
    "        self.train_losses = loss\n",
    "    def on_train_end(self): \n",
    "            self.learner.save(f'{self.model_name}_train_end')\n",
    "            self.model_log['training_loss'] = [str(self.train_losses)]\n",
    "            self.model_log['validation_loss'] = [str(self.val_loss)]\n",
    "            self.model_log['epoch_num'] = [str(self.epoch)]\n",
    "            self.model_log['learning_rate'] = [str(self.learning_rate)]\n",
    "            self.model_log['model_info'] = [w for s in [str(self.learner.model)] for w in s.split('\\n')]\n",
    "            self.model_log['model_info'].append(\"(attention_type): %s\" %self.learner.model.attention_type)\n",
    "            self.model_log['model_info'].append(\"(weight_tie): %s\" %self.learner.model.tied_weight_type)\n",
    "            self.model_log['model_info'].append(\"(pre_trained_vector_type): %s\" %self.learner.model.pre_trained_vector_type)\n",
    "            self.model_log['model_info'].append(\"(teacher_forcing): %s\" %self.learner.model.teacher_forcing)\n",
    "            if self.learner.model.teacher_forcing: self.model_log['model_info'].append(\"(teacher_forcing_prob): %s\" %self.learner.model.force_prob)\n",
    "            with open(f'{self.model_path}/{self.model_name}_train_end_model_log.json', 'w') as d: json.dump(self.model_log, d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TeacherForcingSched(Callback):\n",
    "    def __init__(self, learner, scheduler):\n",
    "        super().__init__()\n",
    "        self.learner = learner\n",
    "        self.scheduler = scheduler\n",
    "        \n",
    "    def on_train_begin(self): \n",
    "        self.learner.model.force_prob = round(self.scheduler.next_val(), 1)\n",
    "        \n",
    "    def on_batch_begin(self): pass\n",
    "    def on_phase_begin(self): pass\n",
    "    def on_epoch_end(self, metrics): \n",
    "        self.learner.model.force_prob = round(self.scheduler.next_val(), 1)\n",
    "        \n",
    "    def on_phase_end(self): pass\n",
    "    def on_batch_end(self, loss):pass\n",
    "    def on_train_end(self): pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Train..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================================================================================================\n",
      "Model log:\n",
      "Seq2SeqRNN(\n",
      "  (pre_trained_vector): Embedding(52380, 200, padding_idx=1)\n",
      "  (encoder_dropout): Dropout(p=0.0)\n",
      "  (encoder_embedding_layer): Embedding(52380, 200, padding_idx=1)\n",
      "  (encoder_rnn): GRU(200, 200, bidirectional=True)\n",
      "  (encoder_vector_layer): Linear(in_features=400, out_features=200, bias=False)\n",
      "  (decoder_dropout): Dropout(p=0.0)\n",
      "  (decoder_embedding_layer): Embedding(52380, 200, padding_idx=1)\n",
      "  (decoder_rnn): GRU(200, 400)\n",
      "  (decoder_output_layer): Linear(in_features=400, out_features=200, bias=False)\n",
      "  (output_layer): Linear(in_features=200, out_features=52380, bias=False)\n",
      "  (encoder_output_layer): Linear(in_features=400, out_features=200, bias=False)\n",
      "  (att_vector_layer): Linear(in_features=400, out_features=200, bias=False)\n",
      ") \n",
      "\n",
      "- attention_type = Luong \n",
      "\n",
      "- weight_tie = three_way \n",
      "\n",
      "- teacher_forcing = True \n",
      " \n",
      "- pre_trained_embedding = glove.6B.200d \n",
      "\n",
      "====================================================================================================\n",
      "\n"
     ]
    }
   ],
   "source": [
    "attention_type='luong'\n",
    "model_luong = Seq2SeqRNN(rnn_type, input_size, embz_size, hidden_size, batch_size, output_size, max_tgt_len,\n",
    "               attention_type, tied_weight_type, pre_trained_vector, pre_trained_vector_type, padding_idx)\n",
    "\n",
    "print('='*100)\n",
    "print('Model log:')\n",
    "print(model_luong, '\\n')\n",
    "print('- attention_type = {} \\n'.format(model_luong.attention_type))\n",
    "print('- weight_tie = {} \\n'.format(model_luong.tied_weight_type))\n",
    "print('- teacher_forcing = {} \\n '.format(model_luong.teacher_forcing)) \n",
    "print('- pre_trained_embedding = {} \\n'.format(model_luong.pre_trained_vector_type)) \n",
    "print('='*100 + '\\n')\n",
    "\n",
    "if USE_GPU:\n",
    "    model_luong.cuda()\n",
    "opt_fn = partial(optim.Adam, betas=(0.8, 0.99))\n",
    "learn_luong = RNN_Learner(model_data, SingleModel(model_luong), opt_fn=opt_fn)\n",
    "learn_luong.crit = seq2seq_loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  0%|          | 0/2500 [00:00<?, ?it/s]"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Widget Javascript not detected.  It may not be installed properly. Did you enable the widgetsnbextension? If not, then run \"jupyter nbextension enable --py --sys-prefix widgetsnbextension\"\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 64%|██████▎   | 1592/2500 [02:54<00:52, 17.29it/s, loss=31.3]"
     ]
    }
   ],
   "source": [
    "learn_luong.lr_find()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  0%|          | 0/2500 [00:00<?, ?it/s]"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Widget Javascript not detected.  It may not be installed properly. Did you enable the widgetsnbextension? If not, then run \"jupyter nbextension enable --py --sys-prefix widgetsnbextension\"\n"
     ]
    },
    {
     "ename": "ValueError",
     "evalue": "Expected input batch_size (16) to match target batch_size (15).",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-46-fa923c6e52a8>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0msched\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mDecayScheduler\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mDecayType\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mLINEAR\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcycle_len\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0mteach_forcer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTeacherForcingSched\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlearn_luong\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msched\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0mlearn_luong\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcycle_len\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcycle_len\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0muse_clr\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstepper\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mSeq2SeqStepper\u001b[0m\u001b[0;34m,\u001b[0m           \u001b[0mcallbacks\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m \u001b[0mteach_forcer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbest_model\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m/home/ellfae/anaconda3/lib/python3.6/site-packages/fastai/learner.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, lrs, n_cycle, wds, **kwargs)\u001b[0m\n\u001b[1;32m    285\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msched\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    286\u001b[0m         \u001b[0mlayer_opt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_layer_opt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlrs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 287\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit_gen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlayer_opt\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn_cycle\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    288\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    289\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mwarm_up\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwds\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/home/ellfae/anaconda3/lib/python3.6/site-packages/fastai/learner.py\u001b[0m in \u001b[0;36mfit_gen\u001b[0;34m(self, model, data, layer_opt, n_cycle, cycle_len, cycle_mult, cycle_save_name, best_save_name, use_clr, use_clr_beta, metrics, callbacks, use_wd_sched, norm_wds, wds_sched_mult, use_swa, swa_start, swa_eval_freq, **kwargs)\u001b[0m\n\u001b[1;32m    232\u001b[0m             \u001b[0mmetrics\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmetrics\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcallbacks\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcallbacks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreg_fn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreg_fn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mclip\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclip\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfp16\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfp16\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    233\u001b[0m             \u001b[0mswa_model\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mswa_model\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0muse_swa\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mswa_start\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mswa_start\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 234\u001b[0;31m             swa_eval_freq=swa_eval_freq, **kwargs)\n\u001b[0m\u001b[1;32m    235\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    236\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mget_layer_groups\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_layer_groups\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/home/ellfae/anaconda3/lib/python3.6/site-packages/fastai/model.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(model, data, n_epochs, opt, crit, metrics, callbacks, stepper, swa_model, swa_start, swa_eval_freq, **kwargs)\u001b[0m\n\u001b[1;32m    127\u001b[0m             \u001b[0mbatch_num\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    128\u001b[0m             \u001b[0;32mfor\u001b[0m \u001b[0mcb\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mcallbacks\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mcb\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mon_batch_begin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 129\u001b[0;31m             \u001b[0mloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel_stepper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mV\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mV\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mepoch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    130\u001b[0m             \u001b[0mavg_loss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mavg_loss\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mavg_mom\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mloss\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mavg_mom\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    131\u001b[0m             \u001b[0mdebias_loss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mavg_loss\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mavg_mom\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mbatch_num\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-31-352c45a63233>\u001b[0m in \u001b[0;36mstep\u001b[0;34m(self, xs, y, epoch)\u001b[0m\n\u001b[1;32m      5\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0moutput\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mxtra\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0moutput\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      6\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzero_grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m         \u001b[0mloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mraw_loss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcrit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      8\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreg_fn\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreg_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mxtra\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mraw_loss\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      9\u001b[0m         \u001b[0mloss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/home/ellfae/anaconda3/lib/python3.6/site-packages/torch/nn/functional.py\u001b[0m in \u001b[0;36mcross_entropy\u001b[0;34m(input, target, weight, size_average, ignore_index, reduce, reduction)\u001b[0m\n\u001b[1;32m   1548\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0msize_average\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mreduce\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1549\u001b[0m         \u001b[0mreduction\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_Reduction\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlegacy_get_string\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msize_average\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreduce\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1550\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mnll_loss\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlog_softmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minput\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mweight\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mignore_index\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreduction\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1551\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1552\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/home/ellfae/anaconda3/lib/python3.6/site-packages/torch/nn/functional.py\u001b[0m in \u001b[0;36mnll_loss\u001b[0;34m(input, target, weight, size_average, ignore_index, reduce, reduction)\u001b[0m\n\u001b[1;32m   1403\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0minput\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mtarget\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1404\u001b[0m         raise ValueError('Expected input batch_size ({}) to match target batch_size ({}).'\n\u001b[0;32m-> 1405\u001b[0;31m                          .format(input.size(0), target.size(0)))\n\u001b[0m\u001b[1;32m   1406\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mdim\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1407\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_C\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_nn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnll_loss\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minput\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mweight\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_Reduction\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_enum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreduction\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mignore_index\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mValueError\u001b[0m: Expected input batch_size (16) to match target batch_size (15)."
     ]
    }
   ],
   "source": [
    "# Luong Attention model\n",
    "lr=1e-3\n",
    "model_name = f'{model_luong.rnn_type}_{model_luong.attention_type}_rev_{rev}'.lower()\n",
    "cycle_len=15\n",
    "best_model = BestModelCheckPoint(learn_luong, model_data.path, model_name, lr)\n",
    "tb_logger=0\n",
    "sched = DecayScheduler(DecayType.LINEAR, cycle_len, 0.5, 0.1)\n",
    "teach_forcer = TeacherForcingSched(learn_luong, sched)\n",
    "learn_luong.fit(lr, 1, cycle_len=cycle_len, use_clr=(20,10), stepper=Seq2SeqStepper, \\\n",
    "          callbacks=[ teach_forcer, best_model])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Output Results\n",
    "Now we will inspect what the model has learned and how good it is performing the text summarization. Keep in mind that we didn't use the entire dataset and we only trained for a few epochs, so results may not be optimal. You can try to improve the code in various ways to get better results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.ticker as ticker\n",
    "\n",
    "# function for plotting the attention weights\n",
    "def plot_attention(attention, sentence, predicted_sentence):\n",
    "    fig = plt.figure(figsize=(10,10))\n",
    "    ax = fig.add_subplot(1, 1, 1)\n",
    "    ax.matshow(attention, cmap='viridis')\n",
    "    fontdict = {'fontsize': 14}\n",
    "    ax.set_xticklabels([''] + sentence, fontdict=fontdict, rotation=90)\n",
    "    ax.set_yticklabels([''] + predicted_sentence, fontdict=fontdict)\n",
    "    ax.xaxis.set_major_locator(ticker.MultipleLocator(1))\n",
    "    ax.yaxis.set_major_locator(ticker.MultipleLocator(1))\n",
    "    plt.show()\n",
    "    \n",
    "    \n",
    "def generate(x, y, m):\n",
    "    probs = m.model(V(x))\n",
    "    preds, attention, encoder_embedding = to_np(probs[0].max(2)[1]), to_np(probs[1].squeeze(1)), to_np(probs[2])\n",
    "    sentence = ' '.join([index_to_string[o] for o in x[:,0].data.cpu().numpy() if o != 1])\n",
    "    result = ' '.join([index_to_string[o] for o in preds[:,0] if o!=1])\n",
    "    orig = ' '.join([index_to_string[o] for o in y[:,0].data.cpu().numpy() if o != 1])\n",
    "    print('Input: {}'.format(sentence), '\\n')\n",
    "    print('Original summary: {}'.format(orig), '\\n')\n",
    "    print('Predicted summary: {}'.format(result))\n",
    "    \n",
    "    # only print sentence (x) and result (predictions)\n",
    "    attention_plot = attention[:len(result.split(' ')), :len(sentence.split(' '))]\n",
    "    plot_attention(attention_plot, sentence.split(' '), result.split(' '))\n",
    "    return preds, attention, encoder_embedding"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "# load the model\n",
    "attention_type='luong'\n",
    "model_luong = Seq2SeqRNN(rnn_type, input_size, embz_size, hidden_size, batch_size, output_size, max_tgt_len,\n",
    "               attention_type, tied_weight_type, pre_trained_vector, pre_trained_vector_type, padding_idx)\n",
    "if USE_GPU:\n",
    "    model_luong.cuda()\n",
    "learn_luong = RNN_Learner(model_data, SingleModel(model_luong))\n",
    "learn_luong.load('gru_luong_rev_1_train_end')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "Input: us president george w. bush called friday for congress to act quickly on a stimulus plan worth around # # # billion dollars to revive an economy that some fear is on the brink of recession . _eos_ \n",
      "\n",
      "Original summary: bush urges economic stimulus of one percent of gdp _eos_ \n",
      "\n",
      "Predicted summary: bush urges some on on stimulus on _eos_\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "Input: south korean share prices closed # . # # percent higher wednesday with early gains driven by an overnight wall street rally tapering off due to a lack of follow - through support , dealers said . _eos_ \n",
      "\n",
      "Original summary: seoul shares close # . # # percent higher _eos_ \n",
      "\n",
      "Predicted summary: seoul shares close # . # # percent higher _eos_\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "Input: italy 's auto giant fiat will lay off between # , # # # and # , # # # workers temporarily in may and june to cut production following slackened demand on the domestic market . _eos_ \n",
      "\n",
      "Original summary: italian auto maker to cut vehicle production _eos_ \n",
      "\n",
      "Predicted summary: auto auto to cut cut # # # _eos_\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Luong (Global Dot) Attention\n",
    "x,y = next(iter(model_data.trn_dl))\n",
    "for i in range(3):\n",
    "    print(i)\n",
    "    preds, attention, encoder_embedding = generate(x.transpose(1,0)[i].unsqueeze(1).contiguous(), \n",
    "                                                   y.transpose(1,0)[i].unsqueeze(1), \n",
    "                                                   learn_luong)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Analysis\n",
    "One of the immediate problems we can observe from the generated summaries is that they are not coherent and there are a lot of repeated phrases or words. Those are very common problems in this type of models and that is why text summarization is still an active area of research. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## References\n",
    "- [Scheduled Sampling for Sequence Prediction with Recurrent Neural Networks](https://arxiv.org/abs/1506.03099)\n",
    "- [Using the Output Embedding to Improve Language Models](https://arxiv.org/pdf/1608.05859.pdf)\n",
    "- [Abstractive Text Summarization using Sequence-to-sequence RNNs and Beyong](https://arxiv.org/pdf/1602.06023.pdf)\n",
    "- [Effective Approaches to Attention-based Neural Machine Translation\n",
    "](https://arxiv.org/pdf/1508.04025.pdf)\n",
    "- [Neural Machine Translation by Jointly Learning to Align and Translate](https://arxiv.org/abs/1409.0473)\n",
    "- [Learn more about teacher forcing in RNNs](https://machinelearningmastery.com/teacher-forcing-for-recurrent-neural-networks/)\n",
    "- [Your TL;DR by an AI: A Deep Reinforced Model for Abstractive Summarization](https://www.salesforce.com/products/einstein/ai-research/tl-dr-reinforced-model-abstractive-summarization/)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Credit\n",
    "Please note that the credit for this notebook goes to GitHub username [alesee](https://github.com/alesee/abstractive-text-summarization). I merely added a few more explanations to the code sections and added a few minor fixes. I thank the author for releasing the code, which is hosted [here](https://github.com/alesee/abstractive-text-summarization/blob/master/abstractive-text-summ.ipynb)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
